#ifndef COMMON_INC
#define COMMON_INC

// gl C bindings
#include <GL/glxew.h>
#include <GL/glx.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>

// C lib std libs
#include <unistd.h>
#include <sys/time.h>

// C++ std libs
#include <iostream>
#include <list>
#include <cmath>
#include <list>
#include <string>

using namespace std;

// OpenGL Abstraction layer
namespace GLA {
	class Qualifier {
		public:
			const string name;
			const bool acceptDefault;
			inline virtual bool global() { return true; }
			inline virtual bool param() { return true; }
			inline virtual bool local() { return true; }
			inline Qualifier(const string _name = "", bool def = false) : name(_name), acceptDefault(def) {}
			static const Qualifier NoQual, Const;
			static const Qualifier &Uniform, &Attribute, &Varying, &In, &Out, &InOut;
			friend basic_ostream<char>& operator << (basic_ostream<char>& out, const Qualifier& q) { out << q.name; }
	};
	class GlobalQual : public Qualifier {
		public:
			inline GlobalQual(string s): Qualifier(s) {}
			inline virtual bool global() { return true; }
			inline virtual bool param() { return false; }
			inline virtual bool local() { return false; }
			static const GlobalQual Uniform, Attribute, Varying;
	};
	class ParamQual : public Qualifier {
		public:
			inline ParamQual(string s): Qualifier(s) {}
			inline virtual bool global() { return false; }
			inline virtual bool param() { return true; }
			inline virtual bool local() { return false; }
			static const ParamQual In, Out, InOut;
	};
	
	/*class Type {};
	class ScalarType: public Type;
	ScalarType Bool, Int, Float;
	Type Mat2, Mat3, Mat4;
	class VectorType: public Type;
	class ComplexType: public Type;
	ComplexType Struct, Array;*/
		
	class Data {
		/// TODO: copy/ref constructors, ref-count, cons from std string/structs
		protected:
			void _alloc() { data = (void*) new char[bytes]; }
		public:
			int bytes;
			void* data;
			Data() : bytes(0), data(0) {}
			Data(int n) : bytes(n) { _alloc(); }
			~Data() { free(); }
			void alloc() { free(); _alloc(); }
			void free() { if(data) delete[] data; }
	};
	
	class CachePolicy {
	
	};
	class Cache {
		public:
			Data* data;
			CachePolicy policy;
	};
	
	
	class DimensionSettings {
		public:
			int size;
			bool tile;
	};
	class ImageHeader {
		public:
			int dims;
			DimensionSettings* dim;
			GLenum format;
	};
	class TextureSettings {
		public:
			bool tile;
	};
	class Texture {
		ImageHeader header;
		TextureSettings settings;
		Cache cache;
		// Transfert Settings
	};
	
};


namespace Meta {
	template <typename T> class Storage {
		public:
			virtual const T& get() const = 0;
			virtual void set(const T&) = 0;
			operator const T& () const { return get(); }
			//friend basic_ostream<char>& print (basic_ostream<char>& out, const PropertyStorage<T> &sto) { out << sto.get(); }
	};
	template <typename T> class Inplace : public Storage<T> {
		protected:
			T v;
		public:
			virtual inline const T& get() const { return v; }
			virtual inline void set(const T& x) { v = x; }
	};
	template <typename T> class External : public Storage<T> {
		protected:
			T* v;
		public:
			virtual inline const T& get() const { return *v; }
			virtual inline void set(const T& x) { *v = x; }
			void setStorage(T* p) { v = p; }
			void setStorage(T& p) { v = &p; }
	};
	// TODO: Getter/Setter storage
	template < typename T > class Notifier {
		public: virtual void trigger(const T& value) = 0;
	};
	template < typename T > class NoNotif : Notifier<T> {
		public: virtual inline void trigger(const T& value) {};
	};
	template < typename T, typename Flag = bool > class DynamicFlag : Notifier<T> {
		protected: Flag* flag;
		public:
			virtual inline void trigger(const T& value) { if(flag) *flag = true; };
			void setFlag(Flag* f) { flag = f; }
	};
	template < typename T, typename Flag = bool > class StaticFlag : Notifier<T> {
		protected: Flag& flag;
		public:
			virtual inline void trigger(const T& value) { flag = true; };
			StaticFlag(Flag& f) : flag(f) {}
	};
	template < typename T, typename Flag = bool > class ConsoleNotifier : Notifier<T> {
		public:
			virtual inline void trigger(const T& value) { cout << "changed" << endl; };
	};

	#define ATTR public:static const
	template < typename > class mustCompare { ATTR bool value = false; };
	template<> class mustCompare<bool> { ATTR bool value = true; };
	template<> class mustCompare<int> { ATTR bool value = true; };
	template<> class mustCompare<char> { ATTR bool value = true; };
	template<> class mustCompare<float> { ATTR bool value = true; };
	template<> class mustCompare<double> { ATTR bool value = true; };

	template < typename T, bool comparable = mustCompare<T>::value > class Comparator {
		ATTR bool noCheck = true;
		ATTR bool diff(const T&a, const T&b) {return false; }
	};
	template < typename T > class Comparator < T, true > {
		ATTR bool noCheck = false;
		ATTR bool diff(const T&a, const T&b) {return a != b; }
	};

	#define PROP_COMMON \
		const T& operator= (const T& v) { if(Comp::noCheck || Comp::diff(Storage::get(), v)) { set(v); trigger(v); } return v; } \
		const char* name; \
		friend basic_ostream<char>& operator << (basic_ostream<char>& out, const Property &p) { if(p.name) out << p.name << " = "; out << p.get(); }
	template < typename T, typename Notifier = NoNotif<T>, typename Storage = Inplace<T>, typename Comp = Comparator<T> > 
	class Property : public Storage, public Notifier {
		public:
			PROP_COMMON
			Property() : name(0) {}
			Property(const char* n) : name(n) {}
			Property(const T& v) : name(0) { set(v); }
			Property(const char* n, const T& v) : name(n) { set(v); }
		
	};

	template < typename T, typename Storage, typename Comp, typename Flag > 
	class Property < T, StaticFlag<T, Flag>, Storage, Comp>: public Storage, public StaticFlag<T, Flag> {
		public:
			PROP_COMMON
			Property(Flag& f) : StaticFlag<T, Flag>(f) {}
			Property(const T& v, Flag& f) : StaticFlag<T, Flag>(f) { set(v); }
			Property(const char* n, Flag& f) : name(n), StaticFlag<T, Flag>(f) {}
			Property(const char* n, const T& v, Flag& f) : name(n), StaticFlag<T, Flag>(f) { set(v); }		
	};
	
	#define prop_declare(type, name) Meta::Property<type, Meta::StaticFlag<type, bool > > name
	#define prop_config(name, value, flag) (#name, value, flag)
	#define prop_init(name, value, flag) name prop_config(name, value, flag)
	#define prop_define(type, name, value, flag) prop_declare(type, name) prop_config(name, value, flag)
};
#endif
